package sk.ygor.raynor.rebind.servicefactory

import com.google.gwt.core.ext.{GeneratorContext, TreeLogger, Generator}
import com.google.gwt.user.rebind.{SourceWriter, ClassSourceFileComposerFactory}
import sk.ygor.raynor.rebind.service.JSONServiceGenerator
import java.lang.String
import sk.ygor.xtemplate.XTemplate
import sk.ygor.raynor.client.service.{JSONServiceFactory, JSONService}
import sk.ygor.raynor.client.json.{JSONSerializer, JSONSerializationException}
import sk.ygor.raynor.rebind.common.ScalaGeneratorWorker

class JSONServiceFactoryGenerator extends Generator {
  def generate(logger: TreeLogger, context: GeneratorContext, typeName: String) =
    new ServiceFactoryGeneratorWorker(logger, context, typeName).generate
}

private class ServiceFactoryGeneratorWorker(logger: TreeLogger, context: GeneratorContext, inputTypeName: String)
        extends ScalaGeneratorWorker(logger, context, inputTypeName) {
  def generateHeader(composer: ClassSourceFileComposerFactory) {
    composer.addImplementedInterface(inputTypeName)
    composer.addImplementedInterface(classOf[JSONServiceFactory.Configuration].getName().replaceAll("\\$", "\\."))

    composer addImports (
            classOf[JSONService[_]],
            classOf[JSONSerializer[_]]
            )
  }

  val jsonServiceClass = context.getTypeOracle getType classOf[JSONService[_]].getName()

  def generateBody(sourceWriter: SourceWriter) {
    val xtpl = new XTemplate("sk/ygor/raynor/rebind/servicefactory/JSONServiceFactory.xtpl")
    val serviceGenerator = new JSONServiceGenerator()
    for ((method, index) <- inputClassType.getMethods.zipWithIndex) {
      if (!method.getParameters.isEmpty) {
        throw new JSONSerializationException("Must have no parameters: " + method)
      }
      val serviceClassType = method.getReturnType.isClassOrInterface
      if (serviceClassType == null) {
        throw new JSONSerializationException("Must return class or interface: " + method)
      }
      val serviceClassName = serviceClassType.getQualifiedSourceName
      if (!serviceClassType.isAssignableTo(jsonServiceClass) || serviceClassName.equals(jsonServiceClass.getQualifiedSourceName)) {
        throw new JSONSerializationException("Must return an ancestor of JSONService: " + method)
      }
      xtpl.assign("serviceClassName", serviceClassName)
      xtpl.assign("serviceInstance", "service" + index)
      xtpl.assign("generatedServiceClassName", serviceGenerator.generate(logger, context, serviceClassName))
      xtpl.assign("methodName", method.getName)
      xtpl.parse("delegateServiceInvoker")
      xtpl.parse("delegateServiceTransformer")
      xtpl.parse("delegateSerializerTransformer")
      xtpl.parse("service")
    }
    xtpl.parse
    sourceWriter println xtpl.getText
  }


}